#include <ptgui/ptgui.h>
#include <ptgui/pagectl.h>

static ptgui_list_t _mq_list;
void ptgui_msgqueue_system_init()
{
	ptgui_list_init(&_mq_list);
}

struct ptgui_msgqueue_message
{
	struct ptgui_msgqueue_message* next;
};

pt_status_t ptgui_msgqueue_init(ptgui_msgqueue_t mq, void *msgpool, rt_size_t msg_size, rt_size_t pool_size)
{
	size_t index;
	struct ptgui_msgqueue_message* head;

	/* append to mq list */
	ptgui_list_insert(&(_mq_list), &(mq->list));

	/* set message pool */
	mq->msg_pool 	= msgpool;

	/* get correct message size */
	mq->msg_size	= RT_ALIGN(msg_size,  RT_ALIGN_SIZE);
	mq->max_msgs	= pool_size / (mq->msg_size + sizeof(struct ptgui_msgqueue_message));

	/* init message list */
	mq->msg_queue_head = RT_NULL;
	mq->msg_queue_tail = RT_NULL;

	/* init message empty list */
	mq->msg_queue_free = RT_NULL;

	for (index = 0; index < mq->max_msgs; index ++)
	{
		head = (struct ptgui_msgqueue_message*)((rt_uint8_t*)mq->msg_pool +
			index * (mq->msg_size + sizeof(struct ptgui_msgqueue_message)));
		head->next = mq->msg_queue_free;
		mq->msg_queue_free = head;
	}

	/* the initial entry is zero */
	mq->entry		= 0;

	return PT_STATUS_SUCCESS;
}

pt_status_t ptgui_msgqueue_detach(ptgui_msgqueue_t mq)
{
	/* remove from list */
	ptgui_list_remove(&_mq_list, &(mq->list));

	return PT_STATUS_SUCCESS;
}

ptgui_msgqueue_t ptgui_msgqueue_create (const char* name, rt_size_t msg_size, rt_size_t max_msgs)
{
	size_t index;
	ptgui_msgqueue_t mq;
	struct ptgui_msgqueue_message* head;

	/* allocate object */
	mq = (ptgui_msgqueue_t) ptgui_malloc(sizeof(struct ptgui_msgqueue));
	if (mq == RT_NULL) return mq;

	/* append to mq list */
	ptgui_list_insert(&(_mq_list), &(mq->list));

	/* init message queue */

	/* get correct message size */
	mq->msg_size	= RT_ALIGN(msg_size, RT_ALIGN_SIZE);
	mq->max_msgs	= max_msgs;

	/* allocate message pool */
	mq->msg_pool = ptgui_malloc((mq->msg_size + sizeof(struct ptgui_msgqueue_message))* mq->max_msgs);
	if (mq->msg_pool == RT_NULL)
	{
		ptgui_msgqueue_delete(mq);
		return RT_NULL;
	}

	/* init message list */
	mq->msg_queue_head = RT_NULL;
	mq->msg_queue_tail = RT_NULL;

	/* init message empty list */
	mq->msg_queue_free = RT_NULL;

	for (index = 0; index < mq->max_msgs; index ++)
	{
		head = (struct ptgui_msgqueue_message*)((rt_uint8_t*)mq->msg_pool +
			index * (mq->msg_size + sizeof(struct ptgui_msgqueue_message)));
		head->next = mq->msg_queue_free;
		mq->msg_queue_free = head;
	}

	/* the initial entry is zero */
	mq->entry		= 0;

	return mq;
}

pt_status_t ptgui_msgqueue_delete (ptgui_msgqueue_t mq)
{
	ptgui_msgqueue_detach(mq);

	/* free mailbox pool */
	ptgui_free(mq->msg_pool);

	/* delete mailbox object */
	ptgui_free(mq);

	return PT_STATUS_SUCCESS;
}

pt_status_t ptgui_msgqueue_send (ptgui_msgqueue_t mq, void* buffer, rt_size_t size)
{
	struct ptgui_msgqueue_message *msg;

	/* greater than one message size */
	if (size > mq->msg_size) return PT_STATUS_FAILED;

	/* get a free list, there must be an empty item */
	msg = (struct ptgui_msgqueue_message*)mq->msg_queue_free;

	/* message queue is full */
	if (msg == RT_NULL)
	{
		return -RT_EFULL;
	}

	/* move free list pointer */
	mq->msg_queue_free = msg->next;

	/* copy buffer */
	ptgui_memcpy(msg + 1, buffer, size);

	/* link msg to message queue */
	if (mq->msg_queue_tail != RT_NULL)
	{
		/* if the tail exists, */
		((struct ptgui_msgqueue_message*)mq->msg_queue_tail)->next = msg;
	}
	/* the msg is the new tail of list, the next shall be NULL */
	msg->next = RT_NULL;

	/* set new tail */
	mq->msg_queue_tail = msg;

	/* if the head is empty, set head */
	if (mq->msg_queue_head == RT_NULL)mq->msg_queue_head = msg;

	/* increase message entry */
	mq->entry ++;

	return PT_STATUS_SUCCESS;
}

pt_status_t ptgui_msgqueue_urgent(ptgui_msgqueue_t mq, void* buffer, rt_size_t size)
{
	struct ptgui_msgqueue_message *msg;

	/* greater than one message size */
	if (size > mq->msg_size) return PT_STATUS_FAILED;

	/* get a free list, there must be an empty item */
	msg = (struct ptgui_msgqueue_message*)mq->msg_queue_free;

	/* message queue is full */
	if (msg == RT_NULL)
	{
		return -RT_EFULL;
	}

	/* move free list pointer */
	mq->msg_queue_free = msg->next;

	/* copy buffer */
	ptgui_memcpy(msg + 1, buffer, (unsigned short)size);

	/* link msg to the beginning of message queue */
	msg->next = mq->msg_queue_head;
	mq->msg_queue_head = msg;

	/* if there is no tail */
	if (mq->msg_queue_tail == RT_NULL) mq->msg_queue_tail = msg;

	/* increase message entry */
	mq->entry ++;

	return PT_STATUS_SUCCESS;
}

pt_status_t ptgui_msgqueue_recv(ptgui_msgqueue_t mq, void* buffer, rt_size_t size)
{
	struct ptgui_msgqueue_message *msg;

	/* mq is empty */
	if (mq->entry == 0)
	{
		return PT_STATUS_FAILED;
	}

	/* get message from queue */
	msg = (struct ptgui_msgqueue_message*) mq->msg_queue_head;

	/* move message queue head */
	mq->msg_queue_head = msg->next;

	/* reach queue tail, set to NULL */
	if (mq->msg_queue_tail == msg) mq->msg_queue_tail = RT_NULL;

	/* copy message */
	ptgui_memcpy(buffer, msg + 1,
		size > mq->msg_size? (unsigned short)(mq->msg_size) : (unsigned short)size);

	/* put message to free list */
	msg->next = (struct ptgui_msgqueue_message*)mq->msg_queue_free;
	mq->msg_queue_free = msg;

	/* decrease message entry */
	mq->entry --;

	return PT_STATUS_SUCCESS;
}
